<?php

namespace App\Game\Logic;


use App\Game\Conf\GameProgress;
use App\Game\Conf\MainCmd;
use App\Game\Conf\SubCmd;
use App\Game\Core\AStrategy;
use App\Game\Core\Message;
use App\Game\Core\Packet;
use App\Game\Core\Room;
use App\Model\User;
use App\Service\Lock;
use App\Task\CreateRoomTask;

class JoinGame extends AStrategy
{

    /**
     * 加入游戏房间，带房间号和不带房间号
     */
    public function exec()
    {
        $game_conf = config('game');
        $account = $this->_params['data']['account'] ? $this->_params['data']['account'] : $this->_params['userinfo']['account'];
        $fd = $this->_params['userinfo']['fd'];
        if (!$account) {
            $msg = ['account' => $account, 'room_no' => null, 'status' => 2, 'info' => '未获取到用户信息'];
            Message::pushDataToUser($account,$msg,SubCmd::SUB_GAME_ROOM_CREATE_RESP,0);
            return;
        }
        //防止重复操作
        if (!Lock::lock($account . '_join_game', 3)) {
            return;
        }

        $playing_room = RooM::checkPlaying($account);
        if ($playing_room) {
            $msg = ['account' => $account, 'room_no' => $playing_room, 'status' => 1, 'info' => '有正在进行的游戏房间'];
            Message::pushDataToUser($account,$msg,SubCmd::SUB_GAME_ROOM_CREATE_RESP,0);
            return;
        }

        $score = $this->checkScore($account);
        if (!$score) {
            $msg = ['account' => $account, 'room_no' => null, 'status' => 2, 'info' => '积分不足'];
            $data = Packet::packFormat('OK', 0, $msg);
            $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_ROOM_CREATE_RESP);
            server()->push($fd, $data, WEBSOCKET_OPCODE_BINARY);//向用户广播
            return;
        }
        $serv = $this->_params['serv'];
        $room_no = isset($this->_params['data']['room_no']) ? $this->_params['data']['room_no'] : null;
//        ddNode('j-0');
        if (empty($room_no)) {
//            ddNode('j-1');
            // 快速游戏没有房间号，检查正在等待的房间
//            $wait = $this->getWaitingRoom();
            $wait = Room::getWaitRoom();
            if (empty($wait)) {
//                ddNode('j-1.2');
                $task = container()->get(CreateRoomTask::class);
                $task->createRoom($fd, $account, true);
            } else {
//                ddNode('j-1.3');
                // 有正在等待房间
//                $room = array_shift($wait);
                $room_no = $wait['room_no'];
                $waiters = getRoomWait($room_no);
                if (count($waiters) == 0 || count($waiters) == 4) { //没人或者满员调用创建房间
                    $flag = false;
                    //继续尝试3次，如果还是没有等待队列则调用创建
                    for ($i = 0; $i < 3; $i++) {
                        $wait = Room::getWaitRoom();
                        if (empty($wait)) {
                            continue;
                        }
                        $room_no = $wait['room_no'];
                        $waiters = getRoomWait($room_no);
                        if (count($waiters) == 0 || count($waiters) == 4) {
                            continue;
                        }
                        $flag = true;
                        break;
                    }
                    if (!$flag) {
                        $task = container()->get(CreateRoomTask::class);
                        $task->createRoom($fd, $account, true);
                        return;
                    }
                }
                $this->roomLockTrans($serv, $room_no, $account);
                $waiters = getRoomWait($room_no);
                if (count($waiters) < 4) { //人数不够继续回归队列
                    Room::addWaitRoom($room_no, $waiters);
                }
            }
        } else {
//            ddNode('j-2');
            $room_data = getRoomData($room_no);
            if ($room_data) {
//                ddTitle('joing 1 ');
                //查看定时器工作状态，如果已经很久没动过，表明该房间已卡死，此时强制踢出所有用户
                $timer = json_decode($room_data['timer'], true);
                $now = time();
                $gameTime = strtotime($timer['time']);
                if ($now - $gameTime > 20) {
                    ddTitle('joing 2' . " gameTime : $gameTime , now: $now , minuse: " . ($now - $gameTime));
                    $msg = ['status' => '强制退出'];
                    $serv = server();
                    $fd = $this->_params['userinfo']['fd'];
                    pushDataToUser($serv, $fd, $msg, SubCmd::FORCE_QUIT_ROOM_RESP);
                    //清空房间
                    clearRoomDataAll($room_no);
                    return;
                }
            }

            $players = $this->checkWaitMembers($room_no, $account);   // 检查房间人数
//            ddNode('j-1');
            if (($players['len'] >= 4) && ($players['in_room'] == 0)) {
                // 如果等待人数4人，则提示人满了或者正在游戏
                $msg = ['status' => '人满了', 'room_no' => $room_no];
                $data = Packet::packFormat('ERROR', 0, $msg);
                return Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_JOIN_FULL);
            } else {
                if ($players['in_room'] == 1) { //重回游戏
//                    ddTitle('joing 3 ');
                    $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
                    if (redis()->exists($room_data_key)) {
//                        ddTitle('joing 4 ');
                        $msg = ['is_game_over' => 0, 'room_no' => $room_no, 'account' => $account];
                        pushDataToUser($serv, $fd, $msg, SubCmd::SUB_USER_IN_GAME);
                    } else {
//                        ddTitle('joing 5 ');
                        //返回游戏房间数据
                        $this->roomLockTrans($serv, $room_no, $account);
                    }
                } else {
//                    ddTitle('joing 6 ');
                    //返回游戏房间数据
                    $this->roomLockTrans($serv, $room_no, $account);
                }
            }
        }
    }

    /**
     * 锁操作房间数据
     * @param $serv
     * @param $room_no
     * @param $account
     */
    public function roomLockTrans($serv, $room_no, $account)
    {
//        $gameProgressType = getGameProgressType($room_no);
//        if (!checkGameProgress($room_no, [GameProgress::GAME_JOIN,
//                GameProgress::GAME_PREPARE]) || $gameProgressType) { //跳过不在准备阶段的房间或者机器人对局的房间
//            $this->_params['data']['room_no'] = null;
//            $this->exec();
//            return;
//        }
        $user_conf = $this->user_conf;
        $redis = redis();
        $user_room_lock_key = sprintf($user_conf['user_room_lock'], $room_no);   // 锁键
        do {
            // 加锁
            $result = $redis->set($user_room_lock_key, $room_no, ['nx', 'ex' => 3]);  //nx代表当key不存在时设置 ex代表设置过期时间
            if ($result) {
                $this->addUserRoomMap($account, $room_no);      // 绑定用户和房间号
                $this->addRoomUser($account, $room_no);         // 将新加入用户推入等待房间
                $this->addRoomChair($account, $room_no);         // 将新加入用户推入等待房间
                $players = $this->checkWaitMembers($room_no, $account);  //检查房间人数
//                dd('players', $players);
                if (!empty($players['players'])) {
                    $prepareArr = getGamePrepareALL($room_no);
                    foreach ($players['players'] as $k => $v) {
//                        dd('players v', $v);
                        $players['players'][$k]['nickname'] = filterNickname($v['nickname']);
                        $players['players'][$k]['prepare'] = isset($prepareArr[$players['players'][$k]['account']]) ? 1 : 0;
                    }
                }
                $match_players = $this->matchChair($players['players'], $room_no);
//                dd('match_players', $match_players);
                $room_data = ['status' => 'waiting', 'players' => $match_players, 'room_no' =>
                    $room_no,'is_real'=>1];
                $fds = $this->getOtherFdsByRoomNo($room_no);
                $this->roomBroadcast($serv, $room_data, $fds);//向房间人广播
                $redis->del($user_room_lock_key);
                continue;
            } else {
                usleep(300);
            }
        } while (!$result);
    }


    /**
     * 设置机器人计数
     * @param $room_no
     * @param $num
     */
    protected function setRobotLoop($room_no, $num)
    {
        $robot_conf = config('robot');
        $robot_loop_key = sprintf($robot_conf['user_room_robot'], $room_no);
        redis()->set($robot_loop_key, $num);
    }

    /**
     * 检查在等待游戏的房间(正序)
     */
    public function getWaitingRoom()
    {
        $game_conf = $this->game_conf;
        $total_room_key = $game_conf['user_room_total'];
        // 所有房间号
        $total_rooms = redis()->sMembers($total_room_key);
        sort($total_rooms);//排序房间号
        $wait_room_conf = $game_conf['user_room_wait'];
        $room_no = [];
        foreach ($total_rooms as $v) {
            $wait_room_key = sprintf($wait_room_conf, $v);
            if (redis()->exists($wait_room_key)) {
                $gtype = getGameProgressType($v);
                if ($gtype == 1) { //单机
                    continue;
                }
                $wait_room_players = redis()->get($wait_room_key);
                $wait_players_arr = json_decode($wait_room_players, true);
                if (count($wait_players_arr) == 3) {
                    $room_no[] = $v;
                    break;
                } elseif (count($wait_players_arr) == 1) {
                    $room_no[] = $v;
                } elseif (count($wait_players_arr) == 4) {
                    continue;
                } else {
                    $room_no[] = $v;
                }
            }
        }
        return $room_no;
    }

    /**
     * 绑定用户和房间
     * @param $account
     * @param $room_no
     */
    public function addUserRoomMap($account, $room_no)
    {
        $game_conf = $this->game_conf;
        $room_map_conf = $game_conf['user_room'];
        $room_map_key = sprintf($room_map_conf, $account);
        redis()->set($room_map_key, (int)$room_no);// 绑定用户房间号
    }

    /**
     * 绑定房间和用户
     * @param $account
     * @param $room_no
     */
    public function addRoomUser($account, $room_no)
    {
        $game_conf = $this->game_conf;
        $room_wait_conf = $game_conf['user_room_wait'];
        $room_wait_key = sprintf($room_wait_conf, $room_no);
        $json_wait_room = redis()->get($room_wait_key);
        $arr_wait_room = [];
        if ($json_wait_room) {
            $arr_wait_room = json_decode($json_wait_room, true);
            if (!in_array($account, $arr_wait_room)) {
                array_push($arr_wait_room, $account);
            }
        }
        redis()->set($room_wait_key, json_encode($arr_wait_room));// 绑定用户房间号
    }

    /**
     * 绑定桌位号
     * @param $account
     * @param $room_no
     */
    public function addRoomChair($account, $room_no)
    {
        $game_conf = $this->game_conf;
        $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
        $room_chair_data = [];
        if (redis()->exists($room_chair_key)) {
            $room_chair_data = json_decode(redis()->get($room_chair_key), true);
            if (!in_array(1, $room_chair_data)) {
                $room_chair_data[$account] = 1;
            } elseif (!in_array(2, $room_chair_data)) {
                $room_chair_data[$account] = 2;
            } elseif (!in_array(3, $room_chair_data)) {
                $room_chair_data[$account] = 3;
            } elseif (!in_array(4, $room_chair_data)) {
                $room_chair_data[$account] = 4;
            }
        } else {
            $room_chair_data[$account] = 1;
        }
        redis()->set($room_chair_key, json_encode($room_chair_data));// 绑定用户房间号桌位号
    }

    /**
     * 匹配玩家和桌位号
     * @param $players
     * @param $room_no
     * @return mixed
     */
    public function matchChair($players, $room_no)
    {
        $game_conf = $this->game_conf;
        $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
        $room_chair_data = json_decode(redis()->get($room_chair_key), true);
        foreach ($players as $k => $v) {
            $players[$k]['chair_id'] = $room_chair_data[$v['account']];
        }
        return $players;
    }

    /**
     * * 检查用户的积分
     * @param $account
     * @return bool
     */
    public function checkScore($account)
    {
        $user_conf = config('user');
        $user_info = User::userInfo($account);
        if (($user_info['score']) > $user_conf['create_room_score']) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 世界广播
     * @param $serv
     * @param $data
     */
    protected function worldBroadcast($serv, $fds)
    {
        $hallData = $this->hallData();
        $data = Packet::packFormat('OK', 1, $hallData);
        $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_HALL_RESP);
        foreach ($serv->connections as $fd) {
            if (!isset($fds[$fd])) {
                $client_info = $serv->getClientInfo($fd);
                $client[$fd] = $client_info;
                if (isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                    $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
                }
            }
        }
    }

    /**
     * 房间广播
     * @param $serv
     * @param $room_data
     * @param $fds
     */
    protected function roomBroadcast($serv, $room_data, $fds)
    {
        $user_room_data = Packet::packFormat('OK', 1, $room_data);
        $return_data = Packet::packEncode($user_room_data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_WAIT);
        foreach ($fds as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if (isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $return_data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

}